﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using RuanRang.Media.Core.Internal.Interop;
using RuanRang.Media.Core.Media;

namespace RuanRang.Media.Core.Internal
{
    internal class VlcMediaList : IList<BaseMedia>, IDisposable
    {
        /// <summary>
        /// Gets or sets the handler.
        /// </summary>
        /// <value>
        /// The handler.
        /// </value>
        public IntPtr Handler { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="VlcMediaList" /> class.
        /// </summary>
        internal VlcMediaList()
        {
            //empty media list, or NULL on error
            var mIntPtr = LibVlcMediaListInterop.libvlc_media_list_new(IntropHandleManager.LibVlcHandle);
            if (mIntPtr == IntPtr.Zero)
            {
                throw new Exception("can't libvlc_media_list_new, VlcMediaList");
            }
            Handler = mIntPtr;
            if (!IntropHandleManager.MediaListHandles.ContainsKey(Handler))
            {
                IntropHandleManager.MediaListHandles.Add(Handler, this);
            }
        }

        /// <summary>
        /// Indexes the of.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns></returns>
        public int IndexOf(BaseMedia item)
        {
            //position of media instance or -1 if media not found
            return LibVlcMediaListInterop.libvlc_media_list_index_of_item(Handler, item.Handle);
        }

        /// <summary>
        /// Inserts the specified index.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <param name="item">The item.</param>
        public void Insert(int index, BaseMedia item)
        {
            //0 on success, -1 if the media list is read-only
            LibVlcMediaListInterop.libvlc_media_list_insert_media(Handler, item.Handle, index);
        }

        /// <summary>
        /// Removes at.
        /// </summary>
        /// <param name="index">The index.</param>
        public void RemoveAt(int index)
        {
            //0 on success, -1 if the list is read-only or the item was not found
            LibVlcMediaListInterop.libvlc_media_list_remove_index(Handler, index);
        }

        /// <summary>
        /// Gets or sets the <see cref="BaseMedia" /> at the specified index.
        /// </summary>
        /// <value>
        /// The <see cref="BaseMedia" />.
        /// </value>
        /// <param name="index">The index.</param>
        /// <returns></returns>
        public BaseMedia this[int index]
        {
            get
            {
                var mediaHandle = LibVlcMediaListInterop.libvlc_media_list_item_at_index(Handler, index);
                if (IntropHandleManager.MediaHandles.ContainsKey(mediaHandle))
                {
                    return IntropHandleManager.MediaHandles[mediaHandle] as BaseMedia;
                }
                return null;
            }
            set
            {
                if (value.Handle != IntPtr.Zero)
                {
                    LibVlcMediaListInterop.libvlc_media_list_set_media(Handler, value.Handle);
                }
                else
                {
                    throw new Exception("MediaBase Handle cant be null.");
                }
            }
        }

        /// <summary>
        /// Adds the specified item.
        /// </summary>
        /// <param name="item">The item.</param>
        public void Add(BaseMedia item)
        {
            if (IsReadOnly) return;
            //0 on success, -1 if the media list is read-only
            var res = LibVlcMediaListInterop.libvlc_media_list_add_media(Handler, item.Handle);
#if DEBUG
            Trace.WriteLine("LibVlcMediaListInterop.libvlc_media_list_add_media : " + res);
#endif
        }

        /// <summary>
        /// Clears this instance.
        /// </summary>
        public void Clear()
        {
            for (var index = Count - 1; index > 0; index--)
            {
                LibVlcMediaListInterop.libvlc_media_list_remove_index(Handler, index);
            }
        }

        /// <summary>
        /// Determines whether [contains] [the specified item].
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns>
        ///   <c>true</c> if [contains] [the specified item]; otherwise, <c>false</c>.
        /// </returns>
        public bool Contains(BaseMedia item)
        {
            return IndexOf(item) > -1;
        }

        /// <summary>
        /// Copies to.
        /// </summary>
        /// <param name="array">The array.</param>
        /// <param name="arrayIndex">Index of the array.</param>
        public void CopyTo(BaseMedia[] array, int arrayIndex)
        {
            for (var i = 0; i < array.Length; i++)
            {
                if (arrayIndex > i)
                {
                    continue;
                }
                array[i] = this[i];
            }
        }

        /// <summary>
        /// Gets the count.
        /// </summary>
        /// <value>
        /// The count.
        /// </value>
        public int Count
        {
            get
            {
                return LibVlcMediaListInterop.libvlc_media_list_count(Handler);
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance is read only.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is read only; otherwise, <c>false</c>.
        /// </value>
        public bool IsReadOnly
        {
            get
            {
                //1 on readonly, 0 on readwrite
                return LibVlcMediaListInterop.libvlc_media_list_is_readonly(Handler) == 1;
            }
        }

        /// <summary>
        /// Removes the specified item.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns></returns>
        public bool Remove(BaseMedia item)
        {
            var index = IndexOf(item);
            if (index >= 0)
            {
                RemoveAt(index);
                return true;
            }
            return false;
        }

        /// <summary>
        /// Gets the enumerator.
        /// </summary>
        /// <returns></returns>
        public IEnumerator<BaseMedia> GetEnumerator()
        {
            for (var index = 0; index < Count; index++)
            {
                var mediaHandle = LibVlcMediaListInterop.libvlc_media_list_item_at_index(Handler, index);
                if (mediaHandle == IntPtr.Zero) continue;
                if (IntropHandleManager.MediaHandles.ContainsKey(mediaHandle))
                {
                    yield return IntropHandleManager.MediaHandles[mediaHandle] as BaseMedia;
                }
            }
        }

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
        /// </returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        // <summary>
        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        // NOTE: Leave out the finalizer altogether if this class doesn't 
        // own unmanaged resources itself, but leave the other methods
        // exactly as they are. 
        /// <summary>
        /// Finalizes an instance of the <see cref="VlcMediaList" /> class.
        /// </summary>
        ~VlcMediaList()
        {
            // Finalizer calls Dispose(false)
            Dispose(false);
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources.
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // free managed resources

                // unmanaged managed resources
                Dispose(false);
                return;
            }
            IntropHandleManager.MediaListHandles.Remove(Handler);
            LibVlcMediaListInterop.libvlc_media_list_release(Handler);
        }
    }
}
